﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace WpfDrawing
{
    /// <summary>
    /// Window3DBox.xaml 的交互逻辑
    /// </summary>
    public partial class Window3DBox : Window
    {
        public Window3DBox()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {

        }
    }



    public class ThreeDbox : FrameworkElement
    {
        public List<DrawingVisual> LstChild = new List<DrawingVisual>();


        //必须重载这两个方法，不然是画不出来的
        protected override int VisualChildrenCount
        {
            get { return LstChild.Count; }
        }

        // 重载当WPF框架向自己要孩子的时候，返回返回DrawingVisual
        protected override Visual GetVisualChild(int index)
        {
            return LstChild[index];
        }

        #region 支持画框

        DrawingVisual SelectRectVisual = null;
        Point LeftButtonClickPoint = new Point();
        Parallelogram Select_Rect_Parallelogram;
        bool StartDraw = false;
        protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            StartDraw = true;
            CaptureMouse();

            LeftButtonClickPoint= e.GetPosition(this);

            if(SelectRectVisual==null)
            {
                SelectRectVisual = new DrawingVisual();
                this.AddVisualChild(SelectRectVisual);
                LstChild.Add(SelectRectVisual);
            }


            //Console.WriteLine("OnMouseLeftButtonDown");

        }

        protected override void OnPreviewMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.LeftButton == MouseButtonState.Pressed && StartDraw == true)
            {
                var dc = SelectRectVisual.RenderOpen();
                var currPoint = e.GetPosition(this);

                double w = currPoint.X - LeftButtonClickPoint.X;
                double h = currPoint.Y - LeftButtonClickPoint.Y;

                Select_Rect_Parallelogram = new Parallelogram(LeftButtonClickPoint, AngleX, w, h);
                
                dc.DrawGeometry(Brushes.Transparent, new Pen(Brushes.Red, 1), Select_Rect_Parallelogram.PGeometry );
                dc.Close();
            }
            Console.WriteLine("OnMouseMove");
        }


        protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonUp(e);
            StartDraw = false;
            ReleaseMouseCapture();

            ShowSelectUnit();

            var dc = SelectRectVisual.RenderOpen();
            dc.Close();


        }

        //显示选中的结果
        DrawingVisual SelectGridVisual = null;

        private List<Pos> ShowSelectUnit()
        {
            List<Pos> Selected = new List<Pos>();
            if (SelectGridVisual == null)
            {
                SelectGridVisual = new DrawingVisual();
                this.AddVisualChild(SelectGridVisual);
                LstChild.Add(SelectGridVisual);
            }


            GeometryGroup pgGroup = new GeometryGroup();
            foreach (var item in DicUnitRect)
            {
                IntersectionDetail intersectionDetail = Select_Rect_Parallelogram.PGeometry.FillContainsWithDetail(item.Value.PGeometry);

                if(intersectionDetail!= IntersectionDetail.Empty && intersectionDetail!= IntersectionDetail.NotCalculated)
                {
                    pgGroup.Children.Add(item.Value.PGeometry);
                    Selected.Add(item.Key);
                }
            }
  
            var dc = SelectGridVisual.RenderOpen();
            dc.DrawGeometry(Brushes.Transparent, new Pen(Brushes.Red, 1), pgGroup);
            dc.Close();
            return Selected;
        }


        #endregion





        private double AngleX = 45;
        private double LevelHeight = 10;

        private double Unitwdith = 30;
        private double Unitheight = 5;

        private Point CenterPoint = new Point(100, 50);


        public Brush TopBrush
        {
            get { return (Brush)GetValue(TopBrushProperty); }
            set { SetValue(TopBrushProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TopBrush.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TopBrushProperty =
            DependencyProperty.Register("TopBrush", typeof(Brush), typeof(ThreeDbox), new PropertyMetadata(Brushes.Blue));

        public Brush RightBrush
        {
            get { return (Brush)GetValue(RightBrushProperty); }
            set { SetValue(RightBrushProperty, value); }
        }

        public static readonly DependencyProperty RightBrushProperty =
            DependencyProperty.Register("RightBrush", typeof(Brush), typeof(ThreeDbox), new PropertyMetadata(Brushes.LightBlue));

        public Brush FrontBrush
        {
            get { return (Brush)GetValue(FrontBrushProperty); }
            set { SetValue(FrontBrushProperty, value); }
        }

        public static readonly DependencyProperty FrontBrushProperty =
            DependencyProperty.Register("FrontBrush", typeof(Brush), typeof(ThreeDbox), new PropertyMetadata(Brushes.LightBlue));


        //单个平行四边形，左下角 相对于左上角的x偏移
        private double Xoffset
        {
            get
            {
                return Unitheight / Math.Tan(AngleX * (Math.PI / 180)) * (-1);
            }
        }


        public ThreeDbox()
        {        
            this.Loaded += Onloaded;

            //Mouse.AddPreviewMouseUpHandler(this, MouseButtonUpEventHandler);

        }

        List<Visual> SelectedUnit = new List<Visual>();
        private void HistTest(Geometry geometry)
        {
            SelectedUnit.Clear();
            GeometryHitTestParameters gp = new GeometryHitTestParameters(geometry);
            VisualTreeHelper.HitTest(this, null,
                new HitTestResultCallback(MyHitTestResult),
                gp);
        }
        // Return the result of the hit test to the callback.
        public HitTestResultBehavior MyHitTestResult(HitTestResult result)
        {
            // Add the hit test result to the list that will be processed after the enumeration.
            //SelectedUnit.Add(result.VisualHit);

            // Set the behavior to return visuals at all z-order levels.
            return HitTestResultBehavior.Continue;
        }

        private bool drawed = false;
        public  void Onloaded(object sender, RoutedEventArgs e)
        {
            if(!drawed)
            {
                Random rm = new Random();
                InitBaseMap(9, 37, Unitwdith, Unitheight, out DicUnitRect);

                foreach(var item in DicUnitRect)
                {
                    if(item.Key.col%2==0)
                        DrawSingleRect(item.Key.row, item.Key.col-1, rm.Next(0, 5));
                }

                drawed = true;
            }

        }

        #region 堆场相关

        private bool LeftToRight = true;

        private 


        #endregion




        Dictionary<Pos, Parallelogram> DicUnitRect=new Dictionary<Pos, Parallelogram>();

        /// <param name="row"></param>
        /// <param name="col"></param>
        /// <param name="wdith">单元格的宽</param>
        /// <param name="height">单元格的宽高</param>
        /// 绘制地图底格
        public void InitBaseMap(int row,int col,double unitwdith,double unitheight,out Dictionary<Pos, Parallelogram> DicRect)
        {

            double x = 0;
            double y = 0;

            DrawingVisual _drawingVisual = new DrawingVisual();
            this.AddVisualChild(_drawingVisual);
            LstChild.Add(_drawingVisual);

            GeometryGroup pgGroup = new GeometryGroup();
            DicRect = new Dictionary<Pos, Parallelogram>();

            var dc = _drawingVisual.RenderOpen();
            //第一行，最后一行 ，第一列 ，最后一列 做备用
            for (int i=0;i<row+2;i++)
            {               
                for (int j=0;j<col+2;j++)
                {
                     x = CenterPoint.X + j * unitwdith + i*Xoffset;
                     y = CenterPoint.Y + i * unitheight;

                    //画行列
                    if ((i == 0|| i==row+1 ) && j > 0 )
                    {
                        if(j<col+1)
                            DrawText(dc, j.ToString(), new Point(x+ Unitwdith/3, y));

                    }
                    else if(i>0 &&(j==0||j==col+1))
                    {
                        if(i<row+1)
                            DrawText(dc, i.ToString(), new Point(x+ Unitwdith / 3, y));

                    }else if(i==0 && j==0)
                    {
                        //
                    }
                    else
                    {
                        Parallelogram rc = new Parallelogram(new Point(x, y), AngleX, unitwdith, unitheight);
                        DicRect.Add(new Pos(i , j ), rc);
                        pgGroup.Children.Add(rc.PGeometry);
                    }


                }

            }

            Width = x + 200;
            Height = y + 200;


            
            dc.DrawGeometry(Brushes.White, new Pen(Brushes.LightGray, 1), pgGroup);
            dc.Close();
        }

        private void DrawText(DrawingContext dc, string text,Point point)
        {
            
            FormattedText formattedText = new FormattedText(text,
                                        CultureInfo.GetCultureInfo("en-us"),
                                        FlowDirection.LeftToRight,
                                        new Typeface("Verdana"),
                                        10,
                                        Brushes.Black);
            dc.DrawText(formattedText, point);
            
        }

        /// <summary>
        /// 绘制单个unit的立体图
        /// </summary>
        /// <param name="rc"></param>
        /// <param name="level">层数</param>
        public void DrawSingleRect(int row, int col, int level,bool isBig=true)
        {
            if(level==0)
            {
                return;
            }
            Pos p = new Pos(row, col);
            if (DicUnitRect.TryGetValue(p, out Parallelogram rc))
            {
                //根据层数，得到偏移后的上层四点,X不变，Y减去yoffset

                double yoffset = LevelHeight * level * (-1);

                Point UpTopLeft = new Point(rc.TopLeft.X, rc.TopLeft.Y + yoffset);

                Point UpBottomLeft = new Point(rc.BottomLeft.X, rc.BottomLeft.Y + yoffset);
                Point UpTopRight;
                Point UpBottomRight;
                if (isBig==false)
                {
                     UpTopRight = new Point(rc.TopRight.X, rc.TopRight.Y + yoffset);
                     UpBottomRight = new Point(rc.BottomRight.X, rc.BottomRight.Y + yoffset);
                }else
                {
                     UpTopRight = new Point(rc.BigBoxTopRight.X, rc.BigBoxTopRight.Y + yoffset);
                     UpBottomRight = new Point(rc.BigBoxBottomRight.X, rc.BigBoxBottomRight.Y + yoffset);
                }



                DrawingVisual _drawingVisual = new DrawingVisual();
                this.AddVisualChild(_drawingVisual);
                LstChild.Add(_drawingVisual);


                
                var dc = _drawingVisual.RenderOpen();

                //dc.PushOpacity (0.3);
                //绘制top面
                PathGeometry pg = new PathGeometry();
                PathFigure figure = new PathFigure();
                figure.StartPoint = UpTopLeft;
                figure.Segments.Add(new LineSegment(UpTopRight, true));
                figure.Segments.Add(new LineSegment(UpBottomRight, true));
                figure.Segments.Add(new LineSegment(UpBottomLeft, true));
                figure.IsClosed = true;
                pg.Figures.Add(figure);

                dc.DrawGeometry(TopBrush, new Pen(Brushes.LightGray, 1), pg);


                //绘制front正面
                pg = new PathGeometry();
                 figure = new PathFigure();
                figure.StartPoint = UpBottomLeft;
                //逆时针坐标分别为
                
                figure.Segments.Add(new LineSegment(UpBottomRight, true));

                if (isBig == false)
                {
                    figure.Segments.Add(new LineSegment(rc.BottomRight, true));
                }
                else
                {
                    figure.Segments.Add(new LineSegment(rc.BigBoxBottomRight, true));
                }
                figure.Segments.Add(new LineSegment(rc.BottomLeft, true));
                figure.IsClosed = true;
                pg.Figures.Add(figure);

                dc.DrawGeometry(FrontBrush, new Pen(Brushes.LightGray, 1), pg);


                //绘制右侧面right
                pg = new PathGeometry();
                figure = new PathFigure();
                figure.StartPoint = UpBottomRight;
                
                figure.Segments.Add(new LineSegment(UpTopRight, true));

                if (isBig == false)
                {
                    figure.Segments.Add(new LineSegment(rc.TopRight, true));
                    figure.Segments.Add(new LineSegment(rc.BottomRight, true));
                }
                else
                {
                    figure.Segments.Add(new LineSegment(rc.BigBoxTopRight, true));
                    figure.Segments.Add(new LineSegment(rc.BigBoxBottomRight, true));
                }

                figure.IsClosed = true;
                pg.Figures.Add(figure);

                dc.DrawGeometry(RightBrush, new Pen(Brushes.LightGray, 1), pg);


                //绘制层分割线
                for(int i=1;i<level;i++)
                {
                    double tmpyoffset = LevelHeight * i * (-1);
                    Point p1 = new Point(rc.BottomLeft.X, rc.BottomLeft.Y + tmpyoffset);
                    Point p2, p3;
                    if (isBig == false)
                    {
                         p2 = new Point(rc.BottomRight.X, rc.BottomRight.Y + tmpyoffset);
                         p3 = new Point(rc.TopRight.X, rc.TopRight.Y + tmpyoffset);
                    }
                    else
                    {
                        p2 = new Point(rc.BigBoxBottomRight.X, rc.BigBoxBottomRight.Y + tmpyoffset);
                        p3 = new Point(rc.BigBoxTopRight.X, rc.BigBoxTopRight.Y + tmpyoffset);
                    }


                    pg = new PathGeometry();
                    figure = new PathFigure();
                    figure.StartPoint = p1;

                    figure.Segments.Add(new LineSegment(p2, true));
                    figure.Segments.Add(new LineSegment(p3, true));
                    figure.IsClosed = false;
                    pg.Figures.Add(figure);
                    dc.DrawGeometry(null, new Pen(Brushes.LightGray, 0.5), pg);
                }


                dc.Close();
            }

        }

    }

    public class Pos
    {
        public int row;
        public int col;
        public Pos(int prow, int pcol)
        {
            row = prow;
            col = pcol;
        }


        public override bool Equals(object obj)
        {
            if(obj is Pos)
            {
                Pos tmp = obj as Pos;
                if (row == tmp.row && col == tmp.col)
                    return true;
            }
            return false;
        }

        public override int GetHashCode()
        {
            return row*100+col;
        }
    }


    public class Parallelogram
    {

        public Point TopLeft { get; set; }

        public double Width;
        public double Height;
        public double AngleX = 45;

        private double Xoffset
        {
            get
            {
                return   Height / Math.Tan(AngleX*(Math.PI / 180)) * (- 1);
            }
        }

        public  Parallelogram(Point topleft, double Angle, double width, double height)
        {
            
            AngleX = Angle;
            Width = width;
            Height = height;

            TopLeft = topleft;
            BottomLeft= new Point(topleft.X + Xoffset, topleft.Y+ height);

            TopRight = new Point(topleft.X + width, topleft.Y);
            BottomRight = new Point(TopRight.X + Xoffset, TopRight.Y + height);

            BigBoxTopRight = new Point(topleft.X + 2*width, topleft.Y);
            BigBoxBottomRight = new Point(BigBoxTopRight.X + Xoffset, BigBoxTopRight.Y + height);

            PGeometry = DrawingUnitGeometry(this);
        }

        private PathGeometry DrawingUnitGeometry(Parallelogram RC)
        {

            PathGeometry pg = new PathGeometry();
            PathFigure figure = new PathFigure();
            figure.StartPoint = RC.TopLeft;
            //逆时针坐标分别为
            figure.Segments.Add(new LineSegment(RC.BottomLeft, true));
            figure.Segments.Add(new LineSegment(RC.BottomRight, true));
            figure.Segments.Add(new LineSegment(RC.TopRight, true));
            figure.IsClosed = true;
            pg.Figures.Add(figure);
            return pg;
        }


        public PathGeometry PGeometry { get; private set; }
        public Point TopRight { get; set; }
        public Point BottomRight { get; set; }
        public Point BottomLeft { get; set; }

        //大箱需要宽*2
        public Point BigBoxTopRight { get; set; }
        public Point BigBoxBottomRight { get; set; }

    }

    public class DrawingVisualExtension
    {


        public static int GetRow(DependencyObject obj)
        {
            return (int)obj.GetValue(RowProperty);
        }

        public static void SetRow(DependencyObject obj, int value)
        {
            obj.SetValue(RowProperty, value);
        }

        // Using a DependencyProperty as the backing store for Row.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RowProperty =
            DependencyProperty.RegisterAttached("Row", typeof(int), typeof(DrawingVisualExtension), new PropertyMetadata(0));





        public static int GetColumn(DependencyObject obj)
        {
            return (int)obj.GetValue(ColumnProperty);
        }

        public static void SetColumn(DependencyObject obj, int value)
        {
            obj.SetValue(ColumnProperty, value);
        }

        // Using a DependencyProperty as the backing store for Column.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ColumnProperty =
            DependencyProperty.RegisterAttached("Column", typeof(int), typeof(DrawingVisualExtension), new PropertyMetadata(0));




        public static Rect GetRect(DependencyObject obj)
        {
            return (Rect)obj.GetValue(RectProperty);
        }

        public static void SetRect(DependencyObject obj, Rect value)
        {
            obj.SetValue(RectProperty, value);
        }

        // Using a DependencyProperty as the backing store for Rect.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RectProperty =
            DependencyProperty.RegisterAttached("Rect", typeof(Rect), typeof(DrawingVisualExtension), new PropertyMetadata(null));



    }



}
